/**
* @file DSMInterface.cpp
* Common defines and typedefs used for accessing DSM for Twain Applications.
* @author TWAIN Working Group
* @date October 2007
*/

#include "commontwain.h"
#include "dsm.h"

#ifdef _WINDOWS
#define kTWAIN_DSM_DLL_NAME "twain_32.dll"
//#define kTWAIN_DSM_DLL_NAME "C:\\Users\\robi\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\w0ijd28r.dev\\extensions\\Bamboofox@RubenChakhmakhchyan\\plugins\\TWAINDSM2.dll" //".dll"
#endif


#ifdef TWH_CMP_MSC
HMODULE
#else
void*
#endif
g_pDSMLibrary = 0;
int g_dsmRefCount = 0; // the dsm library reference count

DSMENTRYPROC g_pDSM_Entry = 0;    /**< global pointer to the DSM entry point */

#ifndef TWH_CMP_MSC
TW_ENTRYPOINT g_dsmEntryPoint = { 0 };  /**< global pointer to the TWAIN entry point structure */
#endif


#ifdef TWH_CMP_GNU
#include <dlfcn.h>
#endif

TW_UINT16 _DSM_Entry( pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, TW_UINT32 DG, TW_UINT16 DAT, TW_UINT16 MSG, TW_MEMREF pData )
{
	TW_UINT16 ret = TWRC_FAILURE;

	//if ( !g_pDSMLibrary && !loadDSMLibrary() )
	//{
	//	// could not load the DSM
	//	//return 0;   /* todo: are we sure of this return? */
	//	throw "CANNOT LOAD DSM LIBRARY";
	//}

	if ( g_pDSM_Entry )
	{
		//TW_IDENTITY twid = { 0 }; pDest = &twid;
		//if ( !pDest ) pDest = pOrigin;
		ret = ( *g_pDSM_Entry )( pOrigin, pDest, DG, DAT, MSG, pData );
	}

	return ret;
}

bool loadDSMLibrary()
{
	// check if already opened
	if ( g_pDSMLibrary )
	{
		g_dsmRefCount++;
		return true;
	}
#ifdef TWH_CMP_GNU
	char *error;
#endif //TWH_CMP_GNU

	if ( ( g_pDSMLibrary = ::LoadLibraryA( kTWAIN_DSM_DLL_NAME ) ) != NULL )
	{
		//if ( ( g_pDSM_Entry = ( DSMENTRYPROC )GetProcAddress( g_pDSMLibrary, "DSM_Entry" ) ) == NULL )
		if ( ( g_pDSM_Entry = ( DSMENTRYPROC ) ::GetProcAddress( g_pDSMLibrary, MAKEINTRESOURCEA( 1 ) ) ) == NULL )
		{
#ifdef TWH_CMP_MSC // dlsym returning NULL is not an error on Unix
			// could not find DSM_Entry function in DSM
			return false; 
#endif //TWH_CMP_MSC
		}
#ifdef TWH_CMP_GNU
		if ((error = dlerror()) != 0)
		{
			cerr << "App - dlsym: " << error << endl;
			return false;
		}
#endif //TWH_CMP_GNU
	}
	else
	{
		// could not load DSM
#ifdef TWH_CMP_GNU
		cerr << "App - dlopen: " << dlerror() << endl;
#endif //TWH_CMP_GNU
		DWORD err = GetLastError();
		return false;
	}

#ifdef TWH_CMP_GNU

#endif

	return true;
}

void unloadDSMLibrary()
{
	if ( g_pDSMLibrary )
	{
		g_dsmRefCount--;
		if ( g_dsmRefCount <= 0 )
		{
			::FreeLibrary( g_pDSMLibrary );
			g_pDSMLibrary = NULL;
			g_pDSM_Entry = NULL;
			g_dsmRefCount = 0;
		}
	}
}


// The following functions are defined in the DSM2,
// For backwards compatibiltiy on windows call the default function
TW_HANDLE _DSM_Alloc( TW_UINT32 _size )
{
#ifndef TWH_CMP_MSC
	if ( g_dsmEntryPoint.DSM_MemAllocate )
	{
		return g_dsmEntryPoint.DSM_MemAllocate( _size );
	}
#else
	return ::GlobalAlloc( GPTR, _size );
#endif

	return 0;
}


void _DSM_Free( TW_HANDLE _hMemory )
{
#ifndef TWH_CMP_MSC
	if ( g_dsmEntryPoint.DSM_MemFree )
	{
		return g_dsmEntryPoint.DSM_MemFree( _hMemory );
	}
#else
	::GlobalFree( _hMemory );
#endif

	return;
}


TW_MEMREF _DSM_LockMemory( TW_HANDLE _hMemory )
{
#ifndef TWH_CMP_MSC
	if ( g_dsmEntryPoint.DSM_MemLock )
	{
		return g_dsmEntryPoint.DSM_MemLock( _hMemory );
	}
#else
	return ( TW_MEMREF ) ::GlobalLock( _hMemory );
#endif

	return 0;
}


void _DSM_UnlockMemory( TW_HANDLE _hMemory )
{
#ifndef TWH_CMP_MSC
	if ( g_dsmEntryPoint.DSM_MemUnlock )
	{
		return g_dsmEntryPoint.DSM_MemUnlock( _hMemory );
	}	
#else
	::GlobalUnlock( _hMemory );
#endif

	return;
}

